package com.erdos.common.pagination;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ibatis.sqlmap.engine.execution.SqlExecutor;
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate;
import com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback;
import com.ibatis.sqlmap.engine.scope.ErrorContext;
import com.ibatis.sqlmap.engine.scope.RequestScope;
import com.ibatis.sqlmap.engine.scope.SessionScope;


public class LimitSqlExecutor extends SqlExecutor {

	private static final Log logger = LogFactory.getLog(LimitSqlExecutor.class);

	private Dialect dialect = new OracleDialect();

	private ThreadLocal<Integer> threadLocalSize = new ThreadLocal<Integer>();

	private boolean enableLimit = true;

	public Dialect getDialect() {
		return dialect;
	}

	public void setDialect(Dialect dialect) {
		this.dialect = dialect;
	}

	public boolean isEnableLimit() {
		return enableLimit;
	}

	public void setEnableLimit(boolean enableLimit) {
		this.enableLimit = enableLimit;
	}

	public void executeQuery(RequestScope request, Connection conn, String sql,
			Object[] parameters, int skipResults, int maxResults,
			RowHandlerCallback callback) throws SQLException {
		if ((skipResults != NO_SKIPPED_RESULTS || maxResults != NO_MAXIMUM_RESULTS)
				&& supportsLimit()) {
			int rowsCount = 0;
			String csql = dialect.getCountSqlString(sql);
			if (parameters != null) {
				rowsCount = executeQuery(request, parameters, conn, csql,
						callback);
				setTotalRowsCount(rowsCount);
			}

			if (skipResults >= rowsCount) {
				skipResults = rowsCount - skipResults;
			}
			sql = dialect.getLimitSqlString(sql, skipResults, maxResults);
			//System.out.println("调试信息："+sql);
			if (logger.isDebugEnabled()) {
				logger.debug(sql);
			}
			skipResults = NO_SKIPPED_RESULTS;
			maxResults = NO_MAXIMUM_RESULTS;
		}
		super.executeQuery(request, conn, sql, parameters, skipResults,
				maxResults, callback);
	}

	private int executeQuery(RequestScope request, Object[] parameters,
			Connection conn, String sql, RowHandlerCallback callback) {
		int rowsCount = 0;
		ErrorContext errorContext = request.getErrorContext();
		errorContext.setActivity("executing query procedure");
		errorContext.setObjectId(sql);
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");
			// Integer rsType = request.getStatement().getResultSetType();
			ps = prepareStatement(request.getSession(), conn, sql);

			if (parameters != null) {
				for (int i = 0; i < parameters.length; i++)
					ps.setObject(i + 1, parameters[i]);
			}

			errorContext.setMoreInfo("Check the parameters (set parameters failed).");
			ps.execute();
			errorContext.setMoreInfo("Check the results (failed to retrieve results).");
			rs = ps.getResultSet();
			// Begin ResultSet Handling
			if (rs != null) {
				while (rs.next()) {
					rowsCount = rs.getInt(OracleDialect.RS_COLUMN);
				}
			}

		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (ps != null) {
				try {
					ps.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			// if(conn!=null){
			// try {
			// conn.close();
			// } catch (SQLException e) {
			// e.printStackTrace();
			// }
			//				
			// }
		}
		return rowsCount;
	}

	public boolean supportsLimit() {
		if (enableLimit && dialect != null) {
			return dialect.supportsLimit();
		}
		return false;
	}

	private static PreparedStatement prepareStatement(SessionScope session,
			Connection conn, String sql) throws SQLException {
		SqlMapExecutorDelegate delegate = ((ExtendedSqlMapClient) session
				.getSqlMapExecutor()).getDelegate();
		if (session.hasPreparedStatementFor(sql)) {
			return session.getPreparedStatement((sql));
		} else {
			PreparedStatement ps = conn.prepareStatement(sql);
			session.putPreparedStatement(delegate, sql, ps);
			return ps;
		}
	}

	public int getTotalRowsCount() {
		Integer size = (Integer) threadLocalSize.get();
		int returnvalue = 0;
		if (size != null) {
			returnvalue = size.intValue();
		}
		return returnvalue;
	}

	public void setTotalRowsCount(int totalRowsCount) {
		threadLocalSize.set(new Integer(totalRowsCount));
	}

}